
let as_to_string st sn = "(" ^ st ^ " " ^ sn ^ ")"

type compareresult = 
    | EQ
    | NE
    | GT
    | GE
    | LT
    | LE

type named = 
    | Var of string
    | Reg of string
    | In  of string
    | Out of string
type valTyp = Uint of int | Bits of int | Sint of int | B | U | S  
type asInt = Int of int | AsInt of named
type range = Range of asInt*asInt | Infinity
type sigTyp = valTyp * range


type 'a expression = 
    | Named  of named
    | Const  of 'a
    | Sum    of 'a expression * 'a expression
    | Prod   of 'a expression * 'a expression
    | Diff   of 'a expression * 'a expression
    | Div    of 'a expression * 'a expression
    | Mod    of 'a expression * 'a expression
    | And    of 'a expression * 'a expression
    | Or     of 'a expression * 'a expression
    | Xor    of 'a expression * 'a expression
    | Comp   of 'a expression * 'a expression * compareresult
    | Not    of 'a expression
    | Bit    of 'a expression * asInt
    | Resize of 'a expression * range

let ( +^ ) a b = Sum(a,b)
let ( *^ ) a b = Prod(a,b)
let ( -^ ) a b = Diff(a,b)
let ( /^ ) a b = Div(a,b)
let ( %^ ) a b = Mod(a,b)
let ( &^ ) a b = And(a,b)
let ( |^ ) a b = Or(a,b)
let ( ^^ ) a b = Xor(a,b)
let ( !^ ) a = Not(a)
let ( ==^ ) a b = Comp(a,b,EQ)
let ( !=^ ) a b = Comp(a,b,NE)

let string_of_named n = match n with 
    | Var(a) -> as_to_string "Var" a
    | Reg(a) -> as_to_string "Reg" a
    | In(a) -> as_to_string "In" a
    | Out(a) -> as_to_string "Out" a    

let string_of_asInt n = match n with
    | Int(a) -> string_of_int a
    | AsInt(a) -> string_of_named a
    
let string_of_range r = match r with
| Range(l,h) ->"[" ^ (string_of_asInt h) ^ " downto " ^ (string_of_asInt l) ^ "]" 
| Infinity -> ""

let string_of_sigTyp (s,r) = match s with
| Uint n -> "U" ^ (string_of_range r) ^ (Printf.sprintf "0x%X" n) 
| Sint n -> "S" ^ (string_of_range r) ^ (Printf.sprintf "0x%X" n) 
| Bits n -> "B" ^ (string_of_range r) ^ (Printf.sprintf "0x%X" n) 
| B -> "B" ^ (string_of_range r) 
| U -> "U" ^ (string_of_range r) 
| S -> "S" ^ (string_of_range r) 

let rec to_string n = 
    let ab_to_string a b c = "(" ^ to_string a ^ " " ^ c ^ " " ^ to_string b ^ ")" in
    match n with
        | Const(a) -> as_to_string "C" (string_of_sigTyp a)
        | Named(a) -> string_of_named a
        | Sum(a,b) -> ab_to_string a b "+"
        | Prod(a,b) -> ab_to_string a b "*"
        | Diff(a,b) -> ab_to_string a b "-"
        | Div(a,b) -> ab_to_string a b "/"
        | Mod(a,b) -> ab_to_string a b "%"
        | And(a,b) -> ab_to_string a b "&"
        | Or(a,b) -> ab_to_string a b "|"
        | Xor(a,b) -> ab_to_string a b "^"
        | Not(a) -> as_to_string "!" (to_string a)
        | Comp(a,b,EQ) -> ab_to_string a b "=="
        | Comp(a,b,NE) -> ab_to_string a b "!="
        | Comp(a,b,GT) -> ab_to_string a b ">"
        | Comp(a,b,GE) -> ab_to_string a b ">="
        | Comp(a,b,LT) -> ab_to_string a b "<"
        | Comp(a,b,LE) -> ab_to_string a b "<="
        | Bit(a,n) -> (to_string a) ^ "[" ^ (string_of_asInt n) ^ "]"
        | Resize(a,Range(l,h)) -> (to_string a) ^ "[" ^ (string_of_asInt h) 
            ^ ".." ^ (string_of_asInt l) ^ "]"
        | Resize(a,Infinity) -> (to_string a)

